package jp.syuriken.snsw.terenaibot.internal;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.zip.ZipEntry;

import jp.syuriken.snsw.terenaibot.BotConfiguration;
import jp.syuriken.snsw.terenaibot.PluginContainer;
import jp.syuriken.snsw.terenaibot.Post;
import jp.syuriken.snsw.terenaibot.plugins.PostHookPlugin;
import jp.syuriken.snsw.terenaibot.plugins.RegisterablePlugin;
import jp.syuriken.snsw.utils.MyResourceBundle;

/**
 * PluginContainerの実装クラス。
 * 
 * @author $Author$
 */
public class PluginContainerImpl implements PluginContainer {
	
	/** クラス名 */
	private static final String CLASS_NAME = "PluginContainerImpl";
	
	private ArrayList<RegisterablePlugin> plugins = new ArrayList<RegisterablePlugin>();
	
	private ArrayList<PostHookPlugin> postHookPlugins = new ArrayList<PostHookPlugin>();
	
	private BotConfiguration configuration;
	

	/**
	 * インスタンスを生成する。
	 * @param conf 設定格納クラス
	 * @param pluginDirName プラグインの入ったディレクトリ名
	 */
	public PluginContainerImpl(BotConfiguration conf, String pluginDirName) {
		File f = new File(pluginDirName);
		String[] files = f.list();
		if (files != null) {
			for (String file2 : files) {
				if (file2.endsWith(".jar")) {
					File file = new File(pluginDirName + File.separator + file2);
					JarFile jar;
					try {
						jar = new JarFile(file);
					} catch (IOException e) {
						conf.getLogger().log(Level.WARNING, e, CLASS_NAME);
						continue;
					}
					InputStream inputStream;
					try {
						ZipEntry entry = jar.getEntry("tbsplugin.inf");
						if (entry != null) {
							inputStream = jar.getInputStream(entry);
						} else {
							conf.getLogger().log(Level.WARNING,
									"プラグインの読み込みでエラーが発生しました: " + file2 + "!/tbsplugin.inf" + "が見つかりませんでした");
							continue;
						}
					} catch (IOException e) {
						conf.getLogger().log(Level.WARNING, e, CLASS_NAME);
						continue;
					}
					MyResourceBundle resourceBundle = new MyResourceBundle(inputStream);
					String cname = resourceBundle.getString("PluginClass");
					URL url;
					try {
						url = file.getCanonicalFile().toURI().toURL();
					} catch (MalformedURLException e) {
						conf.getLogger().log(Level.WARNING, e, CLASS_NAME);
						continue;
					} catch (IOException e) {
						conf.getLogger().log(Level.WARNING, e, CLASS_NAME);
						continue;
					}
					URLClassLoader loader = new URLClassLoader(new URL[] {
						url
					});
					Class<?> cobj;
					try {
						cobj = loader.loadClass(cname);
					} catch (ClassNotFoundException e) {
						conf.getLogger().log(Level.WARNING, "PluginContainer: " + file2 + "の" + cname + "が読み込めませんでした");
						continue;
					}
					Class<?>[] ifnames = cobj.getInterfaces();
					boolean isPluginLoaded = false;
					for (Class<?> ifname : ifnames) {
						if (ifname == RegisterablePlugin.class) {
							RegisterablePlugin plugin;
							try {
								plugin = (RegisterablePlugin) cobj.newInstance();
							} catch (InstantiationException e) {
								conf.getLogger().log(
										Level.WARNING,
										"PluginContainer: " + file2 + "の" + cname + "のインスタンスの作成でエラー: "
												+ e.getLocalizedMessage());
								continue;
							} catch (IllegalAccessException e) {
								conf.getLogger().log(Level.WARNING, e, CLASS_NAME);
								continue;
							}
							plugins.add(plugin);
							conf.getLogger().log(Level.INFO, " PluginContainer: Loaded " + plugin.getPluginName());
							isPluginLoaded = true;
							break;
						}
					}
					if (!isPluginLoaded) {
						conf.getLogger().log(Level.WARNING,
								"PluginContainer: " + file2 + "!class=" + cname + "は直接的にRegisterablePluginを継承していません");
					}
				}
			}
		}
		for (RegisterablePlugin plugin : plugins) {
			if (plugin instanceof PostHookPlugin) {
				postHookPlugins.add((PostHookPlugin) plugin);
			}
		}
	}
	
	@Override
	public void constructPlugins(BotConfiguration configuration) {
		this.configuration = configuration;
		for (RegisterablePlugin plugin : plugins) {
			try {
				plugin.constructPlugin(configuration);
			} catch (RuntimeException e) {
				configuration.getLogger().log(Level.WARNING, e, CLASS_NAME);
			}
		}
	}
	
	@Override
	public void destructPlugins() {
		for (RegisterablePlugin plugin : plugins) {
			try {
				plugin.destructPlugin();
			} catch (RuntimeException e) {
				configuration.getLogger().log(Level.WARNING, e, CLASS_NAME);
			}
		}
	}
	
	@Override
	public void hookAfterPost(Post post) {
		for (PostHookPlugin plugin : postHookPlugins) {
			try {
				plugin.hookAfterPost(post);
			} catch (RuntimeException e) {
				configuration.getLogger().log(Level.WARNING, e, CLASS_NAME);
			}
		}
	}
	
	@Override
	public boolean hookBeforePost(String postString) {
		boolean continuePost = true;
		for (PostHookPlugin plugin : postHookPlugins) {
			try {
				continuePost = plugin.hookBeforePost(postString);
				if (continuePost == false) {
					break;
				}
			} catch (RuntimeException e) {
				configuration.getLogger().log(Level.WARNING, e, CLASS_NAME);
			}
		}
		return continuePost;
	}
}
